; screen.asm
; Low-level screen-handling
;
	.include "common.inc"
	.include "global.inc"
	.include "vicrom.inc"

	.export sg_init
	.export sg_get_block
	.export sg_alloc
	.export sg_free
	.export sg_compress
	.export sg_blank
	.export sg_hide
	.export sg_show
	.export sg_save
	.export sg_restore
	.export sg_address
	.export sg_address2
	.export sg_select_cell

	.export setbkcolor
	.export setfrcolor
	.export setfgcolor

	.export sg_initblp
	.export sg_initbl
	.export sg_hide2
	
	.export sg_bufbeg
	.export sg_bufend
	
	.export scr_dim_x
	.export scr_dim_y
	.export num_cols
	.export num_rows

MAX_BLOCKS = 50
BLANK_CELL = 26
SHADE_CELL = 27
FIRST_CELL = 28
MAX_CELLS = 256-FIRST_CELL

NTSC_COLS = 24
NTSC_ROWS = 15

	.segment "DATA"

num_cols:	.byte MAX_COLS
num_rows:	.byte MAX_ROWS
scr_dim_x:	.byte 208
scr_dim_y:	.byte 255

	.segment "CODE"

sg_init:	; clear Screen
			lda #<COLOR_BASE
			sta reg0
			lda #>COLOR_BASE
			sta reg0+1
			lda #FG_COLOR
			ldx #VIDEO_PAGES
			ldy #0
			jsr fill_pages
			
			; fill screen
			lda #<VIDEO_BASE
			sta reg0
			lda #>VIDEO_BASE
			sta reg0+1
			lda #BLANK_CELL
			ldx #VIDEO_PAGES
			ldy #0
			jsr fill_pages
			
			; setup special cells
			lda	#SHADE_CELL
			ldx #0
			jsr fill_cell
			
			lda #BLANK_CELL
			jsr sg_select_cell
			lda #$55
			ldy #16
@fill_shade:
			eor #$FF
			dey
			sta (reg0), y
			bne @fill_shade
			
			; setup VIC
			lda MACHINE
			cmp #M_NTSC
			beq @ntsc
			
@pal:		lda	#8				; horizontal line start
			sta	VIC_BASE+0
			lda #21				; first line
			sta VIC_BASE+1
			lda #MAX_COLS		; columns
			sta VIC_BASE+2
			lda VIC_BASE+3
			and #$80
			ora #MAX_ROWS*2+1	; rows & char height
			sta VIC_BASE+3
			bne @both

@ntsc:		lda #NTSC_COLS
			sta num_cols
			lda #NTSC_ROWS
			sta num_rows
			lda	#2				; horizontal line start
			sta	VIC_BASE+0
			lda #8				; first line
			sta VIC_BASE+1
			lda #NTSC_COLS		; columns
			sta VIC_BASE+2
			lda VIC_BASE+3
			and #$80
			ora #NTSC_ROWS*2+1	; rows & char height
			sta VIC_BASE+3

@both:		lda #204			; video & character address 
			sta VIC_BASE+5
			lda #BK_COLOR*16+BK_COLOR+8 ; colors
			sta VIC_BASE+15
			
			lda num_cols
			asl
			asl
			asl
			sta scr_dim_x
			lda num_rows
			asl
			asl
			asl
			asl
			bcc @storey
			lda #$FF
@storey:	sta scr_dim_y
			
			ldx #$2C	; opcode bit
			lda num_cols
			lsr
			lsr
			bcs @a2_skip
			stx doadd2
@a2_skip:	lsr
			bcs @a4_skip
			stx doadd4
@a4_skip:	lsr
			bcs @a8_skip
			stx doadd8
@a8_skip:	lsr
			bcs @a16_skip
			stx doadd16
@a16_skip:		
			rts

sg_get_block:
			; A .. block handle -> A .. start, Y .. length, X .. height
			tay
			lda block_list, y
			pha
			lda block_list+1, y
			ldx block_list+2, y
			tay
			pla
			rts

	.segment "DATA"
	
blo_hgt:	.byte $00

	.segment "CODE"
	
sg_alloc:	; A .. size, Y .. height, X .. scratch -> Y .. block handle
			sty blo_hgt
			tax
			clc
			adc next_cell
			bcc @sg_do_alloc
			; garbage collect (compress)
			txa
			pha
			jsr sg_compress
			pla
			tax
			clc
			adc next_cell
			bcc @sg_do_alloc
@sg_alloc_fail:
			ldy #$FF
			rts
@sg_do_alloc:
			ldy #$FD
@sg_next_block:
			iny
			iny
			iny
			lda	block_list, y
			cmp #0
			bne @sg_next_block
			lda next_cell
			sta block_list, y
			lda blo_hgt
			sta block_list+2, y
			txa
			sta block_list+1, y
			clc
			adc next_cell
			sta next_cell
			lda free_cells
			stx free_cells
			sec
			sbc free_cells
			sta free_cells
			rts
	
sg_free:	; A .. block handle, X/Y scratch
			tay
			lda block_list, y
			beq sg_free_end
			tax
			clc
			adc block_list+1, y
			cmp next_cell
			bne @sg_clear_cell
			stx next_cell	
@sg_clear_cell:
			lda #0
			sta block_list, y
			lda block_list+1, y
			clc
			adc free_cells
			sta free_cells
sg_free_end:
			rts

FREE_BLOCK = 1

sg_compress:
			lda #FREE_BLOCK
			ldx #FIRST_CELL-1
@clrbufl1:	sta scratchbuf1, x
			inx
			cpx next_cell
			bne @clrbufl1
			sta scratchbuf1, x
			ldx #MAX_BLOCKS*3
@sortbuf:	dex
			dex
			dex
			ldy block_list, x 	; get block start
			txa
			sta scratchbuf1, y
			bne @sortbuf
			ldx #FIRST_CELL
@nextblk:	stx pos
@srchl1:	cpx next_cell
			bcs @fin
			lda scratchbuf1, x
			cmp #FREE_BLOCK
			bne @movblk
			inx
			bne @srchl1
			jsr panic
@movblk:				; a .. blk idx, x .. act start, pos .. target start
			tay
			cpx pos
			beq @movptr
			lda pos				; move block 
			sta block_list, y
			jsr blkmov
@movptr:	txa					; calc new start
			clc
			adc block_list+1, y	; 	(add length)
			tax
			lda pos				; calc new target
			clc
			adc block_list+1, y
			sta pos
			bne @srchl1
			jsr panic
@fin:		lda pos
			sta next_cell
			rts

	.segment "DATA"
	
tmpclr:		.res 1	

	.segment "CODE"

setbkcolor:	asl
			asl
			asl
			asl
			sta tmpclr
			lda VIC_BASE+15
			and #$0F
			ora tmpclr
			sta VIC_BASE+15
			rts
			
setfrcolor: and #$07
			sta tmpclr
			lda VIC_BASE+15
			and #$F8
			ora tmpclr
			sta VIC_BASE+15
			rts

setfgcolor:	ldx #0
@clrloop:	sta COLOR_BASE, x
			sta COLOR_BASE+256, x
			dex
			bne @clrloop
			rts

blkmov:		; move pixel data around in chargen memory
			; Y ... block index , A ... target start, X .. source start, target < source
			pha
			txa
			pha
			tya
			pha
			tsx
		
			lda $0103, x		; target (A)
			jsr sg_select_cell	; set reg0 to target char
			lda reg0			; reg1 := reg0
			sta reg1
			lda reg0+1
			sta reg1+1
			lda $0102,x			; source (X)
			jsr sg_select_cell	; set reg0 to source char
			
			ldy $0101, x		; block (Y)
			lda	block_list+1, y	; length 
			sta bcnt
	
@blkmva:	ldy #0	
@blkmv1:	lda (reg0), y		; copy one char
			sta (reg1), y
			iny
			cpy #16
			bne @blkmv1
			
			clc					; advance pointers
			lda reg0
			adc #16
			sta reg0
			bcc @nhr0
			inc reg0+1
			clc
@nhr0:		lda reg1
			adc #16
			sta reg1
			bcc @nhr1
			inc reg1+1
@nhr1:		dec bcnt
			bne @blkmva
		
			ldy $0101, x		; block (Y)
			lda	block_list+1, y	; length 
			sta bcnt
			ldy #0
@lpmv1:		lda VIDEO_BASE, y
			sec
			sbc $0102, x		; -source
			cmp bcnt			; < len?
			bcs @nocorr1
			adc $0103, x		; +target
			sta VIDEO_BASE, y
@nocorr1:	iny
			bne @lpmv1
			
			ldy #0
@lpmv2:		lda VIDEO_BASE+256, y
			sec
			sbc $0102, x		; -source
			cmp bcnt			; < len?
			bcs @nocorr2
			adc $0103, x		; +target
			sta VIDEO_BASE+256, y
@nocorr2:	iny
			cpy #MAX_COLS*MAX_ROWS-256
			bne @lpmv2
		
			pla
			tay
			pla
			tax
			pla
			
			rts

	.segment "DATA"

pos:
	.byte $00
height:
	.byte $00
bcnt:
	.byte $00
	
	.segment "CODE"

sg_blank:	; A .. handle, X .. col, Y .. row
			jsr prep_block
			lda #BLANK_CELL
char_block:	sta fill_char
@sb_cp_colo:
			lda m_h
			sta a_h
			lda m_y
			sta a_y
@sb_cp_coli:
			lda fill_char
			ldx a_x
			ldy a_y
			jsr	put_char
			inc bptr
			inc a_y
			dec a_h
			bne @sb_cp_coli
			inc a_x
			dec a_w
			bne @sb_cp_colo
			rts

sg_hide:	; A .. handle, X .. col, Y .. row
			jsr prep_block
sg_hide2:	lda #SHADE_CELL
			jmp char_block

sg_show:	; A .. handle, X .. col, Y .. row
			jsr prep_block
	
@ssh_cp_colo:
			lda m_h
			sta a_h
			lda m_y
			sta a_y
@ssh_cp_coli:
			lda fill_char
			inc fill_char
			ldx a_x
			ldy a_y
			jsr	put_char
			
			inc bptr
			inc a_y
			dec a_h
			bne @ssh_cp_coli
			
			inc a_x
			dec a_w
			bne @ssh_cp_colo
		
			rts

sg_initblp:	stx a_x
			sty m_y
			rts

prep_block: ; A .. handle, X .. col, Y .. row
			; result:
			;	a_x		 ... column
			;   m_y		 ... row
			;	m_h		 ... height
			;	a_w		 ... width
			;	len		 ... length + 1
			;	fillchar ... start char
			jsr sg_initblp
			jsr sg_get_block ; -> A .. start, Y .. length, X .. height
			sta fill_char
sg_initbl:	stx m_h
			iny
			sty len
			ldy #1
			txa
@pb_do:		clc
			adc m_h
			cmp len
			bcs @pb_od
			iny
			bcc @pb_do
@pb_od:		sty a_w
			rts

	.segment "DATA"

len:	.byte $00	
bptr:	.byte $00
a_x:	.byte $00
a_y:	.byte $00
m_y:	.byte $00
a_w:	.byte $00
a_h:	.byte $00
m_h:	.byte $00

	.segment "CODE"

prep_cbuf:
	sta reg1
	stx reg1+1
	ldy #BgBuf::left
	lda (reg1), y
	sta a_x
	ldy #BgBuf::top
	lda (reg1), y
	sta m_y
	ldy #BgBuf::width
	lda (reg1), y 
	sta a_w
	ldy #BgBuf::height
	lda (reg1), y 
	sta m_h
	lda #BgBuf::buffer
	sta bptr
	rts		

sg_save:
	; A .. <, X .. >block_buff (x,y,w,h,buff)
	jsr prep_cbuf

@ss_cp_colo:
	lda m_h
	sta a_h
	lda m_y
	sta a_y
	
@ss_cp_coli:
	ldx a_x
	ldy a_y
	jsr	get_char
	ldy bptr
	sta (reg1), y
	
	inc bptr
	inc a_y
	dec a_h
	bne @ss_cp_coli
	
	inc a_x
	dec a_w
	bne @ss_cp_colo

	rts

sg_restore:
	; A .. <, X .. >block_buff
	jsr prep_cbuf

@sr_cp_colo:
	lda m_h
	sta a_h
	lda m_y
	sta a_y
	
@sr_cp_coli:
	ldy bptr
	lda (reg1), y
	ldx a_x
	ldy a_y
	jsr	put_char
	
	inc bptr
	inc a_y
	dec a_h
	bne @sr_cp_coli
	
	inc a_x
	dec a_w
	bne @sr_cp_colo

	rts

	.segment "DATA"
	
video_base:	.word VIDEO_BASE
	
	; self modifying (add_reg0 replacement)
	
xy_to_video:; X / Y -> reg0
			sty reg0
			ldy	#0
			sty reg0+1
			txa
			jsr shift_reg0 ; * 2
doadd2:		jsr add_reg0
			jsr shift_reg0 ; * 4
doadd4:		jsr add_reg0
			jsr shift_reg0 ; * 8
doadd8:		jsr add_reg0
			jsr shift_reg0 ; * 16
doadd16:	jsr add_reg0
			clc
			adc video_base
			sta reg0
			tya
			adc	video_base+1
			sta reg0+1
			rts

	.segment "CODE"
	
shift_reg0:
	; shift left (reg0) once.
	asl reg0
	rol reg0+1
	rts

add_reg0:
	; add (reg0) to A/Y
	clc
	adc reg0
	pha
	tya
	adc reg0+1
	tay
	pla
	rts

fill_pages:
	; reg0 .. page base, X .. num pages, A .. fill byte, Y .. start offset
	sta	(reg0), y
	iny
	bne fill_pages
	inc reg0+1
	dex
	bne fill_pages
	rts

fill_cell:
	; A .. fill index, X ... fill byte, Y .. scratch
	jsr sg_select_cell
	ldy #16
	txa
fill_sm:
	; reg0 .. base addres, A .. fill byte, Y .. num
	dey
	sta (reg0), y
	bne fill_sm
	rts
	
sg_select_cell:
	; A .. cell, Y .. scratch -> reg0 to cell
	sta reg0
	lda #0
	sta reg0+1
	ldy #4
@fc_l1:
	jsr shift_reg0
	dey
	bne @fc_l1
	clc
	lda reg0+1
	adc	#>CHAR_BASE
	sta reg0+1
	rts
	
sg_address:
	; A .. block handle -> A/Y address of pixel data
	jsr sg_get_block
sg_address2:
	jsr sg_select_cell
	lda reg0
	ldy reg0+1
	rts

	.segment	"DATA"

save_char:	.byte $00

	.segment	"CODE"

put_char:	; X/Y .. position, A char
			sta save_char
			cpx num_cols
			bcs @pc_done
			cpy num_rows
			bcs @pc_done
			jsr xy_to_video
			ldy #0
			lda save_char
			sta (reg0), y	
@pc_done:	rts	

get_char:	; X/Y .. position, -> A char
			lda #BLANK_CELL
			cpx num_cols
			bcs @gc_done
			cpy num_rows
			bcs @gc_done
			jsr xy_to_video
			ldy #0
			lda (reg0), y
@gc_done:	rts	

	V_HALF_SIZE = VIDEO_SIZE/2

sg_bufbeg:	; Initialize video buffer and setup ptr for xy_to_video
			lda #BLANK_CELL
			ldx #V_HALF_SIZE
@loop:		dex
			sta video_buf1, x
			sta video_buf2, x
			bne @loop
			lda #<video_buf
			sta video_base
			lda #>video_buf
			sta video_base+1
			rts

sg_bufend:	; Copy video buffer to screen and reset ptr for xy_to_video
			ldx #0
@loop:		lda video_buf1, x
			sta VIDEO_BASE, x
			lda video_buf2, x
			sta VIDEO_BASE+V_HALF_SIZE, x
			inx
			cpx #V_HALF_SIZE
			bne @loop
			lda #<VIDEO_BASE
			sta video_base
			lda #>VIDEO_BASE
			sta video_base+1
			rts	

	.segment "DATA"

fill_char:	.byte BLANK_CELL

free_cells:	.byte MAX_CELLS
next_cell:	.byte FIRST_CELL

block_list:	.repeat MAX_BLOCKS
			.byte $00, $00, $00
			.endrep

video_buf:
video_buf1:	.res V_HALF_SIZE
video_buf2:	.res V_HALF_SIZE
